#include "jcprocesswatcher_p.h"

namespace JXQ
{

JListenThread::JListenThread()
{
	detach();
}

void JListenThread::addWatcher(JProcessWatcher *q_ptr)
{
	m_mutex.lock();
	m_pidMap.emplace(q_ptr->pid(), q_ptr);
	m_mutex.unlock();
}

void JListenThread::removeWatcher(JHandle pid)
{
	m_mutex.lock();
	m_pidMap.erase(pid);
	m_mutex.unlock();
}

void JListenThread::notifiy(JHandle pid, int result)
{
	J_MUTEX_LOCKER(m_mutex);

	auto it = m_pidMap.find(pid);
	if( it == m_pidMap.end() )
		return ;

	auto watcher = it->second;
	J_MUTEX_LOCKER_UNLOCK();

	watcher->d_ptr->m_result = result;
	watcher->d_ptr->m_pid = 0;

	watcher->event(result);
	watcher->finished.send(result);

	watcher->d_ptr->m_condition.notify_all();
}

JProcessWatcherPrivate::JProcessWatcherPrivate(JHandle pid) :
	m_pid(pid)
{

}

/*-----------------------------------------------------------------------------------------*/

JProcessWatcher::JProcessWatcher(JHandle pid) :
	d_ptr(new JProcessWatcherPrivate(pid))
{
	auto &listener = JListenThread::instance();
	listener.addWatcher(this);
	listener.start();
}

JProcessWatcher::~JProcessWatcher()
{
	JListenThread::instance().removeWatcher(d_ptr->m_pid);
	delete d_ptr;
}

int JProcessWatcher::pid() const
{
	return d_ptr->m_pid;
}

bool JProcessWatcher::wait(int *returnNumber, int msecs)
{
	if( d_ptr->m_pid <= 0 )
	{
		*returnNumber = d_ptr->m_result;
		return true;
	}

	std::unique_lock<std::mutex> locker(d_ptr->m_mutex);

	if( msecs > 0 )
	{
		if( d_ptr->m_condition.wait_for(locker, std::chrono::milliseconds(msecs))
			== std::cv_status::timeout )
			return false;
	}
	else
		d_ptr->m_condition.wait(locker);

	*returnNumber = d_ptr->m_result;
	return true;
}

void JProcessWatcher::event(int)
{

}

/*-----------------------------------------------------------------------------------------*/

class J_DECL_HIDDEN JProcessListenerPrivate
{
public:
	void remove(JHandle pid);
	void clear();

public:
	std::atomic_bool m_deleteOnRemove {false};

	std::mutex m_mutex;
	std::map<JHandle, JProcessWatcher*>  m_watcherMap;
};

void JProcessListenerPrivate::remove(JHandle pid)
{
	J_MUTEX_LOCKER(m_mutex);

	auto it = m_watcherMap.find(pid);
	if( it != m_watcherMap.end() )
	{
		delete it->second;
		m_watcherMap.erase(it);
	}
}

void JProcessListenerPrivate::clear()
{
	J_MUTEX_LOCKER(m_mutex);
	auto it = m_watcherMap.begin();

	for(; it!=m_watcherMap.end(); ++it)
		delete it->second;

	m_watcherMap.clear();
}

/*-----------------------------------------------------------------------------------------*/

JProcessListener::JProcessListener() :
	d_ptr(new JProcessListenerPrivate())
{

}

JProcessListener::~JProcessListener()
{
	delete d_ptr;
}

void JProcessListener::addProcess(JProcessWatcher *watcher)
{
	d_ptr->m_mutex.lock();
	JHandle pid = watcher->pid();

	if( d_ptr->m_watcherMap.emplace(pid, watcher).second )
	{
		watcher->finished.subscribe([this, pid](int result)
		{
			d_ptr->remove(pid);
			finished.send(pid, result);
			event(pid, result);
		});
	}
	d_ptr->m_mutex.unlock();
}

void JProcessListener::addProcess(JHandle pid)
{
	addProcess(new JProcessWatcher(pid));
}

void JProcessListener::event(JHandle,int)
{

}

} //namespace JXQ
